package com.dibang.app.mvp.gain;

import android.content.Context;


import com.dibang.app.mvp.dialog.LoadingDialogActivity;
import com.dibang.app.mvp.gain.convert.FastJsonConverterFactory;
import com.dibang.app.mvp.gain.convert.ToStringConverterFactory;
import com.dibang.app.mvp.gain.exception.ExceptionHandler;
import com.dibang.app.mvp.gain.trust.SSLSocketClient;
import com.dibang.app.mvp.smart.Smart;

import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.ObservableTransformer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

/**
 * Created by wxl on 2019/6/26.
 * https://github.com/QMUI/QMUI_Android
 */

public class Gain {


    /**
     * 静态内部类保证全局唯一
     */
    private static class Instance {
        private static Option option = new Option();
        private static Gain request = new Gain();
        private static HashMap<String, Disposable> disposables = new HashMap<>();
    }


    /**
     * 请求接口
     * @return
     */
    public static ApiService api(){
        return Instance.option.apiService;
    }




    /**
     * 取消注册key里的所有订阅<p>
     * key 为请求的实体类的类名 不包含HttpResult包裹类类名
     * <p/>
     *
     * @param key
     */
    public void dispose(String key) {
        if (key != null && Instance.disposables.containsKey(key)) {
            Disposable disposable = Instance.disposables.get(key);
            if (disposable != null && !disposable.isDisposed()) {
                disposable.dispose();
            }
            Instance.disposables.remove(key);
        }
    }


    /**
     * 取消所有订阅
     */
    public void dispose() {
        Set<String> keySet = Instance.disposables.keySet();
        if (!keySet.isEmpty()) {
            for (String s : keySet) {
                dispose(s);
            }
        }
    }


    /**
     * 默认执行
     *
     * @param observable
     * @param callback
     * @param <T>
     */
    public static  <T> void exe(Observable<HttpResult<T>> observable, Callback<T> callback) {
        observable.compose(Gain.get().defaultSchedulers())
                .subscribe(Gain.get().getDefaultSubscriber(callback));
    }


    /**
     * 默认执行,带对话框
     *
     * @param observable
     * @param callback
     * @param <T>
     */
    public static  <T> void load(Observable<HttpResult<T>> observable, Callback<T> callback) {
        observable.compose(Gain.get().defaultDialogSchedulers())
                .subscribe(Gain.get().getDefaultSubscriber(callback));
    }

    /**
     * 默认描述者
     *
     * @param callback
     * @param <T>
     * @return
     */
    public <T> DefaultSubscriber getDefaultSubscriber(Callback<T> callback) {
        return DefaultSubscriber.newInstance(callback, (disposable, clazz) -> {
            if (clazz != null && disposable != null) {
                Instance.disposables.put(clazz.getName(), disposable);
            }
        });
    }

    public static class Option {

        private Context context;

        private static int HTTP_CONNECT_OUT_TIME = 10 * 1000;

        private static String BASE_URL = "";

        private OkHttpClient.Builder builder;

        private ApiService apiService;

        private ExceptionHandler.OnExceptionCallback exceptionCallback;

        private Option() {
            if (builder == null) {
                builder = new OkHttpClient.Builder()
                        .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
                        .retryOnConnectionFailure(true)
                        .sslSocketFactory(SSLSocketClient.getSSLSocketFactory())
                        .hostnameVerifier(SSLSocketClient.getHostnameVerifier())
                        .connectTimeout(HTTP_CONNECT_OUT_TIME, TimeUnit.SECONDS)
                        .writeTimeout(HTTP_CONNECT_OUT_TIME, TimeUnit.SECONDS)
                        .readTimeout(HTTP_CONNECT_OUT_TIME, TimeUnit.SECONDS);
            }
        }


        /**
         * 添加拦截器<p>
         * ● 不需要担心中间过程的响应,如重定向和重试.<p>
         * ● 总是只调用一次,即使HTTP响应是从缓存中获取.<p>
         * ● 观察应用程序的初衷. 不关心OkHttp注入的头信息如: If-None-Match.<p>
         * ● 允许短路而不调用 Chain.proceed(),即中止调用.<p>
         * ● 允许重试,使 Chain.proceed()调用多次.<p>
         *
         * @param interceptor
         * @return
         */
        public Option addInterceptor(Interceptor interceptor) {
            builder.addInterceptor(interceptor);
            Smart.option().addInterceptor(interceptor);
            return this;
        }


        /**
         * 添加网络拦截器<p>
         * ● 能够操作中间过程的响应,如重定向和重试.<p>
         * ● 当网络短路而返回缓存响应时不被调用.<p>
         * ● 只观察在网络上传输的数据.<p>
         * ● 携带请求来访问连接.<p>
         *
         * @param interceptor
         * @return
         */
        public Option addNetworkInterceptor(Interceptor interceptor) {
            builder.addNetworkInterceptor(interceptor);
            Smart.option().addNetworkInterceptor(interceptor);
            return this;
        }


        /**
         * 错误回调
         *
         * @param exceptionCallback
         * @return
         */
        public Option setExceptionInterceptor(ExceptionHandler.OnExceptionCallback exceptionCallback) {
            this.exceptionCallback = exceptionCallback;
            return this;
        }

        /**
         * 创建retrofit
         *
         * @param clazz
         * @param baseUrl
         * @param <T>
         * @return
         */
        private <T> T createApi(Class<T> clazz, String baseUrl) {
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .client(builder.build())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .addConverterFactory(new ToStringConverterFactory())
                    .addConverterFactory(FastJsonConverterFactory.create())
                    .build();
            return retrofit.create(clazz);
        }


        /**
         * 构建Retrofit
         */
        public void build() {
            apiService = createApi(ApiService.class, BASE_URL);
            ExceptionHandler.exceptionCallback(exceptionCallback);
        }


        /**
         * 全局上下文
         *
         * @param context
         * @return
         */
        public Option context(Context context) {
            this.context = context;
            return this;
        }

        /**
         * 超时时间
         *
         * @param outTime
         * @return
         */
        public Option setConnectOutTime(int outTime) {
            Option.HTTP_CONNECT_OUT_TIME = outTime;
            return this;
        }


        /**
         * 基本路径
         *
         * @param baseUrl
         * @return
         */
        public Option baseUrl(String baseUrl) {
            Option.BASE_URL = baseUrl;
            return this;
        }


        /**
         * 获取超时时间
         * @return
         */
        public static int getHttpConnectOutTime() {
            return HTTP_CONNECT_OUT_TIME;
        }


        /**
         * 获取错误回调
         * @return
         */
        public ExceptionHandler.OnExceptionCallback getExceptionCallback() {
            return exceptionCallback;
        }
    }


    /**
     * 请求
     *
     * @return
     */
    public static Gain get() {
        return Instance.request;
    }


    /**
     * 配置
     *
     * @return
     */
    public static Option option() {
        return Instance.option;
    }


    /**
     * 获得默认Observable
     *
     * @param <T>
     * @return
     */
    public <T> ObservableTransformer<T, T> defaultSchedulers() {
        return observable -> observable
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
    }

    /**
     * 获得默认Dialog Observable
     *
     * @param <T>
     * @return
     */
    public <T> ObservableTransformer<T, T> defaultDialogSchedulers() {
        return observable -> observable
                .subscribeOn(Schedulers.newThread())
                .doOnSubscribe(disposable -> {
                    LoadingDialogActivity.show(option().context);
                })
                .observeOn(AndroidSchedulers.mainThread())
                .doFinally((Action) () -> {
                    LoadingDialogActivity.dismiss();
                });
    }
}
